﻿using Platformer_Example.Content.Images.Environment;
using Platformer_Example.Controller.Character.Attack;
using Platformer_Example.Controller.Interfaces;
using Platformer_Example.Controller.Map.Objects;
using Platformer_Example.Controller.Terrain;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Transformable_Engine_v2.Engine;
using Transformable_Engine_v2.Engine.Animation.ColorAnimation;
using Transformable_Engine_v2.Engine.Animation.Trajectory;
using Transformable_Engine_v2.Engine.GameObjects.Containers;
using Transformable_Engine_v2.Engine.GameObjects.Containers.Transformable_controls;
using Transformable_Engine_v2.Engine.GameObjects.Primitives;
using Transformable_Engine_v2.Engine.Global;
using Transformable_Engine_v2.Engine.Graphics;
using Transformable_Engine_v2.Engine.Helper;
using Transformable_Engine_v2.Engine.Transformable_Math.Geometry;

namespace Platformer_Example.Controller.Map
{
    public class Map : Container, IMap
    {
        private List<Warp> _warps;

        public Map()
        {
            BackgroundLayer = new Container();
            TerrainLayer = new Container();
            PlayerLayer = new Container();
            ParticleLayer = new Container();
            ForegroundLayer = new Container();

            #warning ChangeDefaultBackground
            BackgroundLayer.AddChildren(new RepeatingImage
                                            {
                                                Sprite = SpritesheetCastle.GetSprite(SpritesheetCastle.Background.Main),
                                                AreaRectangle = new TE_Rectangle(0, 0, Game.Options.ScreenSize),
                                            });

            AddChildren(BackgroundLayer, TerrainLayer, PlayerLayer, ParticleLayer, ForegroundLayer);
        }

        

        public string Id { get; set; }

        public Container BackgroundLayer { get; private set; }
        public Container PlayerLayer { get; private set; }
        public Container TerrainLayer { get; private set; }
        public Container ParticleLayer { get; private set; }
        public Container ForegroundLayer { get; private set; }

        public static Dictionary<string, Map> MapDictionary { get; set; }

        public void AddTerrain(MapCellData.TerrainType terrain, int type, TE_Vector2 size, TE_Vector2 position)
        {
            BaseTerrain t = BaseTerrain.CreateTerrain(terrain, type, position, size);

            if (t != null)
                TerrainLayer.AddChildren(t);
        }

        public void AddForegroundTerrain(MapCellData.TerrainType terrain, int type, TE_Vector2 size, TE_Vector2 position)
        {
            BaseTerrain t = BaseTerrain.CreateTerrain(terrain, type, position, size);

            if (t != null)
                ForegroundLayer.AddChildren(t);
        }

        public void AddStair(MapCellData.TerrainType terrain, MapCellData.StairType stair, TE_Vector2 position)
        {
            BaseTerrain s = BaseTerrain.CreateStair(terrain, stair, position);
            BaseTerrain t;

            switch (stair)
            {
                case MapCellData.StairType.Stair11Desc:
                case MapCellData.StairType.Stair21Desc:
                case MapCellData.StairType.Stair31Desc:
                    t = BaseTerrain.CreateStairEnd(position.Offset(-1, 0));
                    break;
                case MapCellData.StairType.Stair11:
                    t = BaseTerrain.CreateStairEnd(position.Offset(1, 0));
                    break;
                case MapCellData.StairType.Stair21:
                    t = BaseTerrain.CreateStairEnd(position.Offset(2, 0));
                    break;
                case MapCellData.StairType.Stair31:
                    t = BaseTerrain.CreateStairEnd(position.Offset(3, 0));
                    break;
                default:
                    throw new ArgumentOutOfRangeException("stair");
            }

            TerrainLayer.AddChildren(s, t);
        }

        public void AddLadder(MapCellData.TerrainType terrain, TE_Vector2 size, TE_Vector2 position)
        {
            TerrainLayer.AddChildren(BaseLadder.CreateLadder(terrain, position, size));
        }

        public void AddEnvironnementObjects(string completePath)
        {
            _warps = new List<Warp>();
            if (File.Exists(completePath))
            {
                var commands = new List<string>();
                using (var sr = new StreamReader(completePath))
                {
                    while (!sr.EndOfStream)
                    {
                        commands.Add(sr.ReadLine());
                    }
                }

                foreach (string command in commands)
                {
                    switch (command.Split(' ')[0].ToLower())
                    {
                        case "warp":
                            _warps.Add(new Warp(command));
                            break;
                    }
                }
            }
        }

        public static List<Map> FromDirectory(string directoryName)
        {
            string completePath = string.Format("Content/Map/{0}", directoryName);
            string[] files = Directory.GetFiles(completePath, "*.bmp");

            return files.Select(FromName).ToList();
        }

        private static Map FromName(string completePath)
        {
            var map = new Map
                          {
                              Id = Path.GetFileNameWithoutExtension(completePath),
                          };
            map.AddEnvironnementObjects(completePath.Replace(".bmp", ".env"));
            MapAnalyser.AnalyseData(MapAnalyser.ExtractData(completePath), map);

            return map;
        }

        public IEnumerable<BaseTerrain> GetTerrain()
        {
            return TerrainLayer.Children.OfType<BaseStair>()
                               .Union(TerrainLayer.Children.OfType<BaseTerrain>().Where(x => !(x is BaseStair)))
                               .Union(GetForegroundTerrain()).Where(x => !x.IsVirtual);
        }

        public IEnumerable<BaseTerrain> GetForegroundTerrain()
        {
            return ForegroundLayer.Children.OfType<BaseTerrain>();
        }

        public IEnumerable<BaseLadder> GetLadders()
        {
            return TerrainLayer.Children.OfType<BaseLadder>();
        }

        public void Leave(Container character, bool isObject)
        {
            if (isObject)
                ParticleLayer.Children.Remove(character);
            else
                PlayerLayer.Children.Remove(character.Parent);
        }

        public void Enter(Container character, bool isObject)
        {
            if(isObject)
                ParticleLayer.AddChildren(character);
            else
                PlayerLayer.AddChildren(character.Parent);
        }

        public TE_Rectangle CollidingWithLadder(TE_Rectangle characterCollisionBox)
        {
            IEnumerable<BaseLadder> ladders = GetLadders();
            return (from ladder in ladders
                where CollisionHelper.Intersects(ladder.CollisionArea, characterCollisionBox)
                select ladder.CollisionArea).FirstOrDefault();
        }

        public bool IsCollidingWithForegroundWall(TE_Rectangle characterCollisionArea)
        {
            IEnumerable<BaseTerrain> terrains = GetForegroundTerrain();

            return terrains.Any(terrain => CollisionHelper.Intersects(terrain.CollisionArea, characterCollisionArea));
        }

        public MapPlayerData CheckWarps(TE_Rectangle playerCollisionBox)
        {
            return
                (from warp in _warps
                    where CollisionHelper.Intersects(playerCollisionBox, warp.WarpArea)
                    select warp.TargetArea).FirstOrDefault();
        }

        public void Attack(AttackDescription attackDescription, IAttacker attacker)
        {
            var targets = PlayerLayer.Children.Select(x => x.Children[0]).OfType<IAttackable>().Where(x => attackDescription.IsValidTarget(x, attacker)).Where(x => attackDescription.IsInAttackArea(x, attacker));

            var hit = attackDescription.GetHitboxRectangle(attacker.GetDirection()) +
                    attacker.GetAbsolutePosition();
            RectanglePrimitive hitBox = new RectanglePrimitive
                                            {
                                                Shape = hit.AsGridBoundRectangle(),
                                                FillColor = new Color(Color.White - attacker.GetTeam().GetColor(), 150),
                                                OutlineColor = Color.White,
                                                OutlineThickness = 1,
                                            };
            hitBox.Die(200);
            hitBox.AddAnimations(new ColorAnimation(new LinearEasing(), Color.Transparent, 200));
            ForegroundLayer.AddChildren(hitBox);

            foreach (var attackable in targets)
            {
                Log.LogError("Attack!");
            }
        }

        public IPickable CanPickupItem(TE_Rectangle playerPickupCollisionArea)
        {
            return ParticleLayer.Children.OfType<IPickable>().FirstOrDefault(x => CollisionHelper.Intersects(playerPickupCollisionArea, x.GetPickupCollisionArea()));
        }
    }
}